#pragma once

#include <cstdint>
#include <functional>
#include <vector>
#include <memory>
#include <unordered_map>
#include <sys/timerfd.h>

#include "EventLoop.hpp"
// 注:该时间轮,可同时将多个定时任务放在同一个时间轮中(时间轮的capacity满足其需求即可)
// 实现基本逻辑,将一定容量的容器的顺序位置作为刻度,作为时间延迟坐标,以类似哈希桶的方式,将不同定时任务挂在对应的时间坐标上,
// 当遍历到对应的时间刻度的位置时,将该位置的所有定时任务全部执行.
//
// 执行方式注:将定时任务封装成一个类.将对应的callback放置在该类的析构函数中,
// 当上述刻度到达该位置时,clear()会自动调用析构函数内的callback()
//
// 注意事项:由于该时间轮是基于析构函数(生命周期)的自动调用,会使用到智能指针,
// 又由于定时任务的控制,并不是设定完成后就无法更改只能等其行为,
// 还包含有刷新、取消等操作需求,而这些的实现会使用智能指针,
// 小心shared_ptr的引用循环,weak_ptr就能解决该问题.

using TaskFunc = std::function<void()>;    // 定时器回调函数模板
using ReleaseFunc = std::function<void()>; // 定时器任务完成后,相关资源释放模板
class TimerTask
{
private:
    uint64_t _id;         // 任务ID
    bool _canceled;       // 是否取消
    TaskFunc _cb;         // 定时器回调函数
    ReleaseFunc _release; // 资源释放函数
    uint32_t _timeout;    // 延期时间
public:
    TimerTask(uint64_t id, uint32_t delay, TaskFunc cb, ReleaseFunc re = ReleaseFunc()) : _id(id), _timeout(delay), _cb(cb), _release(re), _canceled(false)
    {
    }

    ~TimerTask()
    {
        if (_canceled == false)
            _cb();
        _release();
    }

public:
    // 资源释放
    void SetRelease(ReleaseFunc re) { _release = re; }
    // 取消任务
    void Cancel() { _canceled = true; }
    // 延期时间
    uint32_t GetTimeout() { return _timeout; }
};

#define FIRSTSECOND 1 // 默认时间轮的第一次超时时间
#define OUTTIME 1     // 默认超时时间（第一次后）

class EventLoop;
class TimerWheel
{
private:
    using TaskPtr = std::shared_ptr<TimerTask>;
    using WeakPtr = std::weak_ptr<TimerTask>;

    std::vector<std::vector<TaskPtr>> _wheel; // 时间轮本体
    std::unordered_map<int, WeakPtr> _timers; // 用于ID查找定时任务本体(使用weak_ptr见开头)
    int _tick;                                // 当前指针指向的时间坐标
    int _capacity;                            // 容量

    EventLoop *_loop;
    int _timerfd;                           // 定时器fd  ->超时后触发事件回调，借用eventloop模块中的任务池（可以不用加锁）
    std::unique_ptr<Channel> _timerChannel; // 用于timefd的事件监控

private:
    // 移除任务（若没有取消，直接移除会导致直接执行）
    void RemoveTimer(uint64_t id)
    {
        if (_timers.count(id))
            _timers.erase(id);
    }

    // 时间轮,执行单位时间
    void RunTimerTask()
    {
        _wheel[_tick].clear();
        _tick = (_tick + 1) % _capacity;
    }

    // timerfd定时器创建
    static int CreateTimerfd(time_t firstSeconds = FIRSTSECOND, time_t timeOut = OUTTIME)
    {
        int timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
        if (timerfd < 0)
        {
            ELOG("TIMERFD CREATE FAILED!");
            abort();
        }
        struct itimerspec itime;
        itime.it_value.tv_sec = firstSeconds; // 第一次超时时间
        itime.it_value.tv_nsec = 0;
        itime.it_interval.tv_sec = timeOut;
        itime.it_interval.tv_nsec = 0; // 第一次超时后，每次超时的间隔时
        timerfd_settime(timerfd, 0, &itime, NULL);
        return timerfd;
    }

    // timerfd读事件
    int ReadTimerfd()
    {
        uint64_t times; // 读到的times是其超时的次数
        // 有可能由于其它事件延迟了timerfd的读取，导致timerfd的多次超时

        int ret = read(_timerfd, &times, 8);
        if (ret < 0)
        {
            ELOG("READ TIMEFD FAILED!");
            abort();
        }
        return times;
    }

    // 超时时间(次数)到达，执行对应任务
    void OnTime()
    {
        int times = ReadTimerfd();
        for (int i = 0; i < times; i++)
            RunTimerTask();
    }

    void TimerAddInLoop(uint64_t id, uint32_t delay, const TaskFunc &cb)
    {
        TaskPtr pt(new TimerTask(id, delay, cb));
        pt->SetRelease(std::bind(&TimerWheel::RemoveTimer, this, id));
        int pos = (_tick + delay) % _capacity;
        _wheel[pos].push_back(pt);
        _timers[id] = WeakPtr(pt);
    }

    //定时任务的刷新/推迟
    void TimerRefreshInLoop(uint64_t id)
    {
        // 通过保存的定时器对象的weak_ptr构造一个shared_ptr出来，添加到轮子中
        auto it = _timers.find(id);
        if (it == _timers.end())
        {
            return; // 没找着定时任务，没法刷新，没法延迟
        }
        TaskPtr pt = it->second.lock(); // lock获取weak_ptr管理的对象对应的shared_ptr
        int delay = pt->GetTimeout();
        int pos = (_tick + delay) % _capacity;
        _wheel[pos].push_back(pt);
    }

    void TimerCancelInLoop(uint64_t id)
    {
        auto it = _timers.find(id);
        if (it == _timers.end())
        {
            return; // 没找着定时任务，没法刷新，没法延迟
        }
        TaskPtr pt = it->second.lock();
        if (pt)
            pt->Cancel();
    }

public:
    TimerWheel(EventLoop *loop,int capacity = 60, int startTick = 0 )
        : _tick(startTick), _capacity(capacity), _wheel(capacity), _loop(loop),
          _timerfd(CreateTimerfd()), _timerChannel(new Channel(_loop, _timerfd))
    {
        _timerChannel->SetReadCallback(std::bind(&TimerWheel::OnTime, this)); // 设置读事件
        _timerChannel->EnableRead();                                          // 启动读事件监控
    }

public:
    // 为了达到去锁，且解决线程安全问题，这里借用eventloop中的runinloop（）接口（该接口可以让任务只在同一个线程内执行）

    // 添加新定时任务
    void TimerAdd(uint64_t id, uint32_t delay, const TaskFunc &cb);

    // 刷新已有定时任务
    void TimerRefresh(uint64_t id);

    // 取消定时任务
    void TimerCancel(uint64_t id);

    /*这个接口存在线程安全问题--这个接口实际上不能被外界使用者调用，只能在模块内，在对应的EventLoop线程内执行*/
    bool HasTimer(uint64_t id)
    {
        auto it = _timers.find(id);
        if (it == _timers.end())
        {
            return false;
        }
        return true;
    }
};
